<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>

</body>
<script>
    //【1】类数组对象{什么是类数组对象}
    // 所谓的类数组对象:
    // 拥有一个 length 属性和若干索引属性的对象
    // 举个例子：
    var array = ['name', 'age', 'sex'];
    var arrayLike = {
        0: 'name',
        1: 'age',
        2: 'sex',
        length: 3
    };
    // 即便如此，为什么叫做类数组对象呢？
    // 那让我们从读写、获取长度、遍历三个方面看看这两个对象。
    //【1-1】读写
    console.log(array[0]); // name
    console.log(arrayLike[0]); // name
    array[0] = 'new name';
    arrayLike[0] = 'new name';
    //【1-2】长度
    console.log(array.length); // 3
    console.log(arrayLike.length); // 3
    //【1-2】遍历
    for (var i = 0, len = array.length; i < len; i++) {
        //    ……
    }
    for (var i = 0, len = arrayLike.length; i < len; i++) {
        // ……
    }
    // 是不是很像？
    // 那类数组对象可以使用数组的方法吗？比如：
    // arrayLike.push('4');
    // 然而上述代码会报错: arrayLike.push is not a function
    // 所以终归还是类数组呐……
    //【2】调用数组方法
    // 如果类数组就是任性的想用数组的方法怎么办呢？
    // 既然无法直接调用，我们可以用 Function.call 间接调用：
    var arrayLike = {
        0: 'name',
        1: 'age',
        2: 'sex',
        length: 3
    }
    Array.prototype.join.call(arrayLike, '&'); // name&age&sex
    Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] 
    // slice可以做到类数组转数组
    Array.prototype.map.call(arrayLike, function(item) {
        return item.toUpperCase();
    });
    // ["NAME", "AGE", "SEX"]
    //【3】类数组转数组
    // 在上面的例子中已经提到了一种类数组转数组的方法，再补充三个：
    var arrayLike = {
        0: 'name',
        1: 'age',
        2: 'sex',
        length: 3
    };
    // 1. slice
    Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"] 
    // 2. splice
    Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"] 
    // 3. ES6 Array.from
    Array.from(arrayLike); // ["name", "age", "sex"] 
    // 4. apply
    Array.prototype.concat.apply([], arrayLike);
    //【4】Arguments对象【argument指的是实参】
    // 接下来重点讲讲 Arguments 对象。
    // Arguments 对象只定义在函数体中，包括了函数的参数和其他属性。在函数体中，arguments 指代该函数的 Arguments 对象。
    // 举个例子：
    function foo(name, age, sex) {
        console.log(arguments);
    }

    foo('name', 'age', 'sex'); //Arguments(3) ["name", "age", "sex", callee: ƒ, Symbol(Symbol.iterator): ƒ]
    //【4】我们可以看到除了类数组的索引属性和length属性之外， 还有一个callee属性， 接下来我们一个一个介绍。
    //【4-1】length属性
    // Arguments对象的length属性， 表示实参的长度， 举个例子：
    function foo(b, c, d) {
        console.log("实参的长度为：" + arguments.length)
    }
    console.log("形参的长度为：" + foo.length)
    foo(1);
    // 形参的长度为：3
    // 实参的长度为：1
    //【4-2】callee属性
    // Arguments 对象的 callee 属性， 通过它可以调用函数自身。
    // 讲个闭包经典面试题使用 callee 的解决方法：
    var data = [];

    for (var i = 0; i < 3; i++) {
        (data[i] = function() {
            debugger
            console.log(arguments.callee.i)
        }).i = i;
    }

    data[0]();
    data[1]();
    data[2]();

    // 0
    // 1
    // 2
    // 接下来讲讲 arguments 对象的几个注意要点：
</script>

</html>